// The above function is supposed to set szBuffer to be aDefault if it can't find the
// file, section, or key. In other words, it always changes the contents of szBuffer.
return OUTPUT_VAR->Assign(szBuffer); // Avoid using the length the API reported because it might be inaccurate if the data contains any binary zeroes, or if the data is double-terminated, etc.
// Note: ErrorLevel is not changed by this command since the aDefault value is returned
// Note: The contents of any of these types can be >64K on NT/2k/XP+ (though that is probably rare):
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
dwRes = 0; // Retained for backward compatibility because values >64K may cause it to fail on Win95 (unverified, and MSDN implies its value should be ignored for the following call).
// MSDN: If lpData is NULL, and lpcbData is non-NULL, the function returns ERROR_SUCCESS and stores
// the size of the data, in bytes, in the variable pointed to by lpcbData.
if (RegQueryValueEx(hRegKey, aValueName, NULL, NULL, NULL, &dwRes) != ERROR_SUCCESS // Find how large the value is.
|| !dwRes) // Can't find size (realistically might never happen), or size is zero.
{
RegCloseKey(hRegKey);
return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // For backward compatibility, indicate success (these conditions should be very rare anyway).
}
// Set up the variable to receive the contents, enlarging it if necessary:
// Since dwRes includes the space for the zero terminator (if the MSDN docs
// are accurate), this will enlarge it to be 1 byte larger than we need,
// which leaves room for the final newline character to be inserted after
// the last item. But add 2 to the requested capacity in case the data isn't
// terminated in the registry, which allows double-NULL to be put in for REG_MULTI_SZ later.
if (output_var.Assign(NULL, (VarSizeType)(dwRes + 2)) != OK)
{
RegCloseKey(hRegKey);
return FAIL; // FAIL is only returned when the error is a critical one such as this one.
}
contents = output_var.Contents(); // This target buf should now be large enough for the result.
result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, (LPBYTE)contents, &dwRes);
RegCloseKey(hRegKey);
if (result != ERROR_SUCCESS || !dwRes) // Relies on short-circuit boolean order.
*contents = '\0'; // MSDN says the contents of the buffer is undefined after the call in some cases, so reset it.
// Above realistically probably never happens; for backward compatibility (and simplicity),
// consider it a success.
else
{
// See ReadRegString() for more comments about the following:
// The MSDN docs state that we should ensure that the buffer is NULL-terminated ourselves:
// "If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, then lpcbData will also
// include the size of the terminating null character or characters ... If the data has the
// REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with the
// proper null-terminating characters. Applications should ensure that the string is properly
// terminated before using it, otherwise, the application may fail by overwriting a buffer."
//
// Double-terminate so that the loop can find out the true end of the buffer.
// The MSDN docs cited above are a little unclear. The most likely interpretation is that
// dwRes contains the true size retrieved. For example, if dwRes is 1, the first char
// in the buffer is either a NULL or an actual non-NULL character that was originally
// stored in the registry incorrectly (i.e. without a terminator). In either case, do
// not change the first character, just leave it as is and add a NULL at the 2nd and
// 3rd character positions to ensure that it is double terminated in every case:
contents[dwRes] = contents[dwRes + 1] = '\0';
if (dwType == REG_MULTI_SZ) // Convert NULL-delimiters into newline delimiters.
{
for (cp = contents;; ++cp)
{
if (!*cp)
{
// Unlike AutoIt3, it seems best to have a newline character after the
// last item in the list also. It usually makes parsing easier:
*cp = '\n'; // Convert to \n for later storage in the user's variable.
if (!*(cp + 1)) // Buffer is double terminated, so this is safe.
// Double null terminator marks the end of the used portion of the buffer.
break;
}
}
// else the buffer is empty (see above notes for explanation). So don't put any newlines
// into it at all, since each newline should correspond to an item in the buffer.
output_var.Length() = (VarSizeType)strlen(contents); // Due to conservative buffer sizes above, length is probably too large by 3. So update to reflect the true length.
return output_var.Close(); // In case it's the clipboard.
case REG_BINARY:
{
dwRes = sizeof(szRegBuffer);
result = RegQueryValueEx(hRegKey, aValueName, NULL, NULL, (LPBYTE)szRegBuffer, &dwRes);
RegCloseKey(hRegKey);
if (result == ERROR_MORE_DATA)
// The API docs state that the buffer's contents are undefined in this case,
// so for no we don't support values larger than our buffer size:
return OK; // Let ErrorLevel tell the story. The output variable has already been made blank.
// Set up the variable to receive the contents, enlarging it if necessary.
// AutoIt3: Each byte will turned into 2 digits, plus a final null:
if (output_var.Assign(NULL, (VarSizeType)(dwRes * 2)) != OK)
return FAIL;
contents = output_var.Contents();
*contents = '\0';
int j = 0;
DWORD i, n; // i and n must be unsigned to work
char szHexData[] = "0123456789ABCDEF"; // Access to local vars might be faster than static ones.
for (i = 0; i < dwRes; ++i)
{
n = szRegBuffer[i]; // Get the value and convert to 2 digit hex